{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import requests\n",
    "import re\n",
    "from lxml import html\n",
    "import time\n",
    "\n",
    "class MyCrawler:\n",
    "    def __init__(self, filename):\n",
    "        self.filename = filename\n",
    "        self.headers = {\n",
    "            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4128.3 Safari/537.36'\n",
    "        }\n",
    "\n",
    "    def download(self, url: str):\n",
    "        r = requests.get(url, headers=self.headers)\n",
    "        return r.text\n",
    "\n",
    "    def extract(self, content, pattern):\n",
    "        res = re.findall(pattern, content)\n",
    "        return res\n",
    "\n",
    "    def save(self, info):\n",
    "        with open(self.filename, 'a', encoding='utf-8') as f:\n",
    "            for item in info:\n",
    "                f.write(''.join(item) + '\\n') \n",
    "\n",
    "    def crawl(self, url: str, pattern: str, headers=None):\n",
    "        if headers:\n",
    "            self.headers.update(headers)\n",
    "        content = self.download(url)\n",
    "        info = self.extract(content, pattern)\n",
    "        self.save(info)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "douban_crawler = MyCrawler('douban_new.txt')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "Last start id =  300\n1 :  挪威的森林\n2 :  挪威的森林\n3 :  当我谈跑步时我谈些什么\n4 :  且听风吟\n5 :  海边的卡夫卡\n6 :  挪威的森林\n7 :  没有色彩的多崎作和他的巡礼之年\n8 :  我的职业是小说家\n9 :  1Q84 BOOK 1\n10 :  刺杀骑士团长\n11 :  当我谈跑步时，我谈些什么（2015典藏版）\n12 :  世界尽头与冷酷仙境\n13 :  海边的卡夫卡\n14 :  海边的卡夫卡\n15 :  且听风吟\n16 :  国境以南 太阳以西\n17 :  1Q84 BOOK 2\n18 :  假如真有时光机\n19 :  1Q84 BOOK 3\n20 :  眠\n21 :  大萝卜和难挑的鳄梨\n22 :  爱吃沙拉的狮子\n23 :  没有女人的男人们\n24 :  寻羊冒险记\n25 :  天黑以后\n26 :  1Q84\n27 :  东京奇谭集\n28 :  斯普特尼克恋人\n29 :  遇到百分之百的女孩\n30 :  世界尽头与冷酷仙境\n31 :  远方的鼓声\n32 :  挪威的森林\n33 :  且听风吟\n34 :  无比芜杂的心绪\n35 :  神的孩子全跳舞\n36 :  舞！舞！舞！\n37 :  袭击面包店\n38 :  世界尽头与冷酷仙境\n39 :  国境以南，太阳以西\n40 :  1973年的弹子球\n41 :  奇鸟行状录\n42 :  萤\n43 :  国境以南 太阳以西\n44 :  遇到百分之百的女孩\n45 :  如果我们的语言是威士忌\n46 :  舞舞舞（上）\n47 :  斯普特尼克恋人\n48 :  如果我们的语言是威士忌\n49 :  雨天 炎天\n50 :  1973年的弹子球\n51 :  舞！舞！舞！\n52 :  1Q84 BOOK1\n53 :  再袭面包店\n54 :  1Q84（全三册）\n55 :  海边的卡夫卡\n56 :  村上广播\n57 :  寻羊冒险记\n58 :  村上春树·美食\n59 :  地下\n60 :  终究悲哀的外国语\n61 :  当我谈跑步时，我谈些什么\n62 :  图书馆奇谈\n63 :  了不起的盖茨比\n64 :  地下\n65 :  地下2\n66 :  猫头鹰在黄昏起飞\n67 :  与小泽征尔共度的午后音乐时光\n68 :  奇鸟行状录\n69 :  1Q84 BOOK2\n70 :  一九七三年的弹子球\n71 :  舞！舞！舞！\n72 :  爵士乐群英谱\n73 :  奇鸟行状录\n74 :  边境 近境\n75 :  1Q84 BOOK3\n76 :  电视人\n77 :  且听风吟\n78 :  寻羊冒险记\n79 :  再袭面包店\n80 :  列克星敦的幽灵\n81 :  朗格汉岛的午后\n82 :  夜半蜘蛛猴\n83 :  奇鸟行状录\n84 :  天黑以后\n85 :  象厂喜剧\n86 :  悉尼！\n87 :  东京奇谭集\n88 :  旋涡猫的找法\n89 :  去中国的小船\n90 :  与小泽征尔共度的午后音乐时光\n91 :  爵士乐群英谱 2\n92 :  国境以南 太阳以西\n93 :  貓頭鷹在黃昏飛翔\n94 :  没有意义就没有摇摆\n95 :  1973年的弹子球\n96 :  萤\n97 :  列克星敦的幽灵\n98 :  永远的少年\n99 :  袭击面包店\n100 :  寻羊冒险记\n101 :  天黑以后\n102 :  挪威的森林\n103 :  舞 ! 舞 ! 舞 !\n104 :  碎片，令人怀念的1980年代\n105 :  猫を棄てる\n106 :  羊男的圣诞节\n107 :  What I Talk About When I Talk About Running\n108 :  神的孩子全跳舞\n109 :  世界尽头与冷酷仙境\n110 :  夜半蜘蛛猴\n111 :  舞！舞！舞！\n112 :  一人称単数\n113 :  舞舞舞（下）\n114 :  村上朝日堂\n115 :  村上朝日堂 嗨嗬!\n116 :  日出国的工厂\n117 :  去中国的小船\n118 :  国境以南太阳以西\n119 :  毛茸茸\n120 :  挪威的森林（电影特别版）\n121 :  当村上春树遇见荣格\n122 :  國境之南．太陽之西\n123 :  象的失踪\n124 :  挪威的森林\n125 :  象厂喜剧\n126 :  旋转木马鏖战记\n127 :  在约定的场所\n128 :  象的失踪\n129 :  终究悲哀的外国语\n130 :  挪威的森林\n131 :  蘭格漢斯島的午後\n132 :  倾听村上春树\n133 :  旋转木马鏖战记\n134 :  村上Ｔ 僕の愛したＴシャツたち\n135 :  1Q84（全三册）\n136 :  挪威的森林\n137 :  然而，很美\n138 :  朗格汉岛的午后\n139 :  挪威的森林\n140 :  爵士乐群英谱\n141 :  村上朝日堂的卷土重来\n142 :  村上さんのところ\n143 :  村上朝日堂嗨嗬!\n144 :  發條鳥年代記（第一部）鵲賊篇\n145 :  挪威的森林\n146 :  沒有色彩的多崎作和他的巡禮之年\n147 :  雨天炎天\n148 :  刺殺騎士團長\n149 :  斯普特尼克恋人\n150 :  洗耳倾听\n151 :  村上春树的别样魅力\n152 :  1973年的彈珠玩具\n153 :  1Q84 BOOK 3\n154 :  挪威的森林\n155 :  遇見100%的女孩\n156 :  雨天．炎天\n157 :  村上朝日堂\n158 :  1Q84 BOOK 3\n159 :  國境之南‧太陽之西\n160 :  沒有女人的男人們\n161 :  村上春树·猫\n162 :  村上春树.旅\n163 :  村上春树·西班牙\n164 :  人造衛星情人\n165 :  爵士群像\n166 :  青春的舞步\n167 :  生日故事集\n168 :  Norwegian Wood\n169 :  海邊的卡夫卡（上）\n170 :  村上春树论\n171 :  村上朝日堂反擊\n172 :  挪威的森林\n173 :  村上春树，去见河合隼雄\n174 :  發條鳥年代記（第二部）預言鳥篇\n175 :  挪威的森林\n176 :  你說，寮國到底有什麼？\n177 :  寻羊冒险记\n178 :  South of the Border, West of the Sun\n179 :  刺殺騎士團長\n180 :  刺殺騎士團長\n181 :  村上春树和我\n182 :  舞舞舞吧（下）\n183 :  聽風的歌\n184 :  地下鐵事件\n185 :  村上朝日堂日记 旋涡猫的找法\n186 :  象的消失\n187 :  What I Talk About When I Talk About Running\n188 :  村上朝日堂是如何锻造的\n189 :  發條鳥年代記（第三部）刺鳥人篇\n190 :  What I Talk About When I Talk About Running\n191 :  Norwegian Wood. Haruki Murakami\n192 :  小澤征爾さんと、音楽について話\n193 :  Sydney!\n194 :  象工場的HAPPY END\n195 :  再见，吾爱\n196 :  村上春樹雜文集\n197 :  世界の終りとハードボイルド・ワンダーランド〈下〉\n198 :  Pinball, 1973\n199 :  村上春树作品集\n200 :  騎士団長殺し 第1部 顕れるイデア編  上\n201 :  騎士団長殺し 第1部 顕れるイデア編 下\n202 :  海辺のカフカ\n203 :  萨哈林旅行记\n204 :  世界の終りとハードボイルド・ワンダーランド 下巻\n205 :  A Wild Sheep Chase\n206 :  給我搖擺，其餘免談\n207 :  爵士群像2\n208 :  舞.舞.舞（下）\n209 :  騎士団長殺し 第2部 遷ろうメタファー編 上\n210 :  睡\n211 :  羊をめぐる冒険（下）\n212 :  我與村上春樹、書，還有畫筆\n213 :  高墙与鸡蛋\n214 :  向西聞記\n215 :  東京．村上春樹．旅\n216 :  Absolutely on Music\n217 :  海邊的卡夫卡（下）\n218 :  懷念的一九八〇年代\n219 :  色彩を持たない多崎つくると、彼の巡礼の年\n220 :  杏のふむふむ\n221 :  眠\n222 :  长眠不醒\n223 :  圖書館奇譚\n224 :  第四只手\n225 :  翻译家村上春树\n226 :  挪威的森林\n227 :  村上收音機\n228 :  What I Talk About When I Talk About Running\n229 :  1Q84 BOOK 2\n230 :  了不起的盖茨比\n231 :  你說，寮國到底有什麼？（精裝版）\n232 :  尋找村上春樹之旅\n233 :  Kafka on the Shore\n234 :  村上朝日堂的卷土重来\n235 :  Dance Dance Dance\n236 :  襲擊麵包店\n237 :  孤岛之鬼\n238 :  邊境．近境\n239 :  奇鸟行状录\n240 :  海辺のカフカ（下）\n241 :  村上收音機\n242 :  袋鼠通信\n243 :  世界末日與冷酷異境\n244 :  おおきなかぶ、むずかしいアボカド  村上ラヂオ2\n245 :  Värittömän miehen vaellusvuodet\n246 :  挪威的森林（上）\n247 :  世界尽头与冷酷仙境\n248 :  Men Without Women\n249 :  遠方的鼓聲\n250 :  过境之鸟\n251 :  聽風的歌\n252 :  和小澤征爾先生談音樂\n253 :  地下鐵事件\n254 :  尋找漩渦貓的方法\n255 :  1Q84（BOOK1+BOOK2  平裝）\n256 :  Colorless Tsukuru Tazaki and His Years of Pilgrimage\n257 :  1Q84 Book 2\n258 :  1Q84 BOOK1\n259 :  挪威的森林\n260 :  一不小心就老了\n261 :  The Elephant Vanishes\n262 :  Colorless Tsukuru Tazaki and His Years of Pilgrimage\n263 :  日本“后战后”时期的精神史寓言\n264 :  白夜行\n265 :  村上先生的愛樂電台\n266 :  藏传佛教绘画史\n267 :  沉默的巡游\n268 :  走ることについて語るときに僕の語ること\n269 :  What I Talk About When I Talk About Running\n270 :  開往中國的慢船\n271 :  安吾人生谈\n272 :  远方的大鼓声\n273 :  挪威的森林\n274 :  挪威的森林\n275 :  了不起的盖茨比\n276 :  關於跑步，我說的其實是……\n277 :  螢火蟲\n278 :  永遠的少年\n279 :  萊辛頓的幽靈\n280 :  羊男的圣诞节\n281 :  电视人\n282 :  女のいない男たち\n283 :  終於悲哀的外國語\n284 :  村上春樹去見河合隼雄（新版）\n285 :  异乡人\n286 :  刺殺騎士團長\n287 :  村上春樹去見河合隼雄\n288 :  跳！跳！跳！\n289 :  村上春樹 雑文集\n290 :  好风长吟\n291 :  听说每颗星都会寂寞\n292 :  Kafka on the Shore\n293 :  夜半蜘蛛猴\n294 :  且听风吟\n295 :  聽風的歌\n296 :  騎士団長殺し 第2部 遷ろうメタファー編 下\n297 :  漫长的告别\n298 :  關於跑步，我說的其實是……\n299 :  Killing Commendatore\n300 :  螢・納屋を焼く・その他の短編\n301 :  如果我們的語言是威士忌\n302 :  レキシントンの幽霊\n303 :  Dance Dance Dance\n304 :  スプートニクの恋人\n305 :  村上收音機2, 大蕪菁、難挑的酪梨\n306 :  風の歌を聴け\n307 :  羊をめぐる冒険（上）\n308 :  村上春树·猫\n309 :  神の子どもたちはみな踊る\n310 :  迴轉木馬的終端\n311 :  开往中国的慢船\n"
    }
   ],
   "source": [
    "page_id = 1\n",
    "last_start = 0\n",
    "number = 0\n",
    "while 1:\n",
    "    start_id = 20 * (page_id - 1)\n",
    "    url = 'https://book.douban.com/tag/%E6%9D%91%E4%B8%8A%E6%98%A5%E6%A0%91?start={}&type=T'.format(start_id)\n",
    "    # print(url)\n",
    "    content = douban_crawler.download(url)\n",
    "    tree = html.fromstring(content)\n",
    "    if page_id == 1:\n",
    "        page_links = tree.xpath(\"//div[@class='paginator']/a[last()]/@href\")\n",
    "        if page_links:\n",
    "            # print(page_links)\n",
    "            last_start = int(re.findall(\"start=(\\d+)\", page_links[0])[0])\n",
    "            print('Last start id = ', last_start)\n",
    "    book_list = tree.xpath('//li[@class=\\'subject-item\\']')\n",
    "    for book in book_list:\n",
    "        bookname = book.xpath('.//h2/a')[0].text.strip()\n",
    "        bookurl = book.xpath('.//h2/a')[0].attrib['href']\n",
    "        bookinfo = book.xpath('.//div[@class=\\'pub\\']')[0].text.strip()\n",
    "        bookintro = 'N/A'\n",
    "        if book.xpath(\".//div[@class='info']/p\"):\n",
    "            bookintro = book.xpath(\".//div[@class='info']/p\")[0].text.strip()\n",
    "        number += 1\n",
    "        print(number, ': ', bookname)\n",
    "    page_id += 1\n",
    "    if start_id ==  last_start:\n",
    "        break\n",
    "    time.sleep(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "%E5%B0%8F%E8%AF%B4\n%E5%A4%96%E5%9B%BD%E6%96%87%E5%AD%A6\n%E6%96%87%E5%AD%A6\n%E7%BB%8F%E5%85%B8\n%E4%B8%AD%E5%9B%BD%E6%96%87%E5%AD%A6\n%E9%9A%8F%E7%AC%94\n%E6%97%A5%E6%9C%AC%E6%96%87%E5%AD%A6\n%E6%95%A3%E6%96%87\n%E6%9D%91%E4%B8%8A%E6%98%A5%E6%A0%91\n%E8%AF%97%E6%AD%8C\n%E7%AB%A5%E8%AF%9D\n%E5%90%8D%E8%91%97\n%E5%84%BF%E7%AB%A5%E6%96%87%E5%AD%A6\n%E5%8F%A4%E5%85%B8%E6%96%87%E5%AD%A6\n%E4%BD%99%E5%8D%8E\n%E7%8E%8B%E5%B0%8F%E6%B3%A2\n%E6%9D%82%E6%96%87\n%E5%BD%93%E4%BB%A3%E6%96%87%E5%AD%A6\n%E5%BC%A0%E7%88%B1%E7%8E%B2\n%E5%A4%96%E5%9B%BD%E5%90%8D%E8%91%97\n%E9%92%B1%E9%92%9F%E4%B9%A6\n%E9%B2%81%E8%BF%85\n%E8%AF%97%E8%AF%8D\n%E8%8C%A8%E5%A8%81%E6%A0%BC\n%E7%B1%B3%E5%85%B0%C2%B7%E6%98%86%E5%BE%B7%E6%8B%89\n%E6%9D%9C%E6%8B%89%E6%96%AF\n%E6%B8%AF%E5%8F%B0\n%E6%BC%AB%E7%94%BB\n%E6%8E%A8%E7%90%86\n%E7%BB%98%E6%9C%AC\n%E4%B8%9C%E9%87%8E%E5%9C%AD%E5%90%BE\n%E9%9D%92%E6%98%A5\n%E6%82%AC%E7%96%91\n%E7%A7%91%E5%B9%BB\n%E8%A8%80%E6%83%85\n%E6%8E%A8%E7%90%86%E5%B0%8F%E8%AF%B4\n%E5%A5%87%E5%B9%BB\n%E6%AD%A6%E4%BE%A0\n%E6%97%A5%E6%9C%AC%E6%BC%AB%E7%94%BB\n%E8%80%BD%E7%BE%8E\n%E9%9F%A9%E5%AF%92\n%E7%BD%91%E7%BB%9C%E5%B0%8F%E8%AF%B4\n%E4%B8%89%E6%AF%9B\n%E7%A7%91%E5%B9%BB%E5%B0%8F%E8%AF%B4\n%E4%BA%A6%E8%88%92\n%E9%98%BF%E5%8A%A0%E8%8E%8E%C2%B7%E5%85%8B%E9%87%8C%E6%96%AF%E8%92%82\n%E9%87%91%E5%BA%B8\n%E5%AE%89%E5%A6%AE%E5%AE%9D%E8%B4%9D\n%E7%A9%BF%E8%B6%8A\n%E9%83%AD%E6%95%AC%E6%98%8E\n%E8%BD%BB%E5%B0%8F%E8%AF%B4\n%E9%AD%94%E5%B9%BB\n%E9%9D%92%E6%98%A5%E6%96%87%E5%AD%A6\n%E5%87%A0%E7%B1%B3\nJ.K.%E7%BD%97%E7%90%B3\n%E5%B9%BE%E7%B1%B3\n%E5%BC%A0%E5%B0%8F%E5%A8%B4\n%E5%8F%A4%E9%BE%99\n%E6%A0%A1%E5%9B%AD\n%E9%AB%98%E6%9C%A8%E7%9B%B4%E5%AD%90\n%E6%B2%A7%E6%9C%88\n%E4%BD%99%E7%A7%8B%E9%9B%A8\n%E8%90%BD%E8%90%BD\n%E5%8E%86%E5%8F%B2\n%E5%BF%83%E7%90%86%E5%AD%A6\n%E5%93%B2%E5%AD%A6\n%E7%A4%BE%E4%BC%9A%E5%AD%A6\n%E4%BC%A0%E8%AE%B0\n%E6%96%87%E5%8C%96\n%E8%89%BA%E6%9C%AF\n%E7%A4%BE%E4%BC%9A\n%E6%94%BF%E6%B2%BB\n%E8%AE%BE%E8%AE%A1\n%E5%AE%97%E6%95%99\n%E6%94%BF%E6%B2%BB%E5%AD%A6\n%E5%BB%BA%E7%AD%91\n%E7%94%B5%E5%BD%B1\n%E6%95%B0%E5%AD%A6\n%E4%B8%AD%E5%9B%BD%E5%8E%86%E5%8F%B2\n%E5%9B%9E%E5%BF%86%E5%BD%95\n%E6%80%9D%E6%83%B3\n%E5%9B%BD%E5%AD%A6\n%E4%BA%BA%E7%89%A9%E4%BC%A0%E8%AE%B0\n%E8%89%BA%E6%9C%AF%E5%8F%B2\n%E4%BA%BA%E6%96%87\n%E9%9F%B3%E4%B9%90\n%E7%BB%98%E7%94%BB\n%E6%88%8F%E5%89%A7\n%E8%A5%BF%E6%96%B9%E5%93%B2%E5%AD%A6\n%E8%BF%91%E4%BB%A3%E5%8F%B2\n%E4%BA%8C%E6%88%98\n%E5%86%9B%E4%BA%8B\n%E4%BD%9B%E6%95%99\n%E8%80%83%E5%8F%A4\n%E8%87%AA%E7%94%B1%E4%B8%BB%E4%B9%89\n%E7%BE%8E%E6%9C%AF\n%E7%88%B1%E6%83%85\n%E6%88%90%E9%95%BF\n%E7%94%9F%E6%B4%BB\n%E6%97%85%E8%A1%8C\n%E5%BF%83%E7%90%86\n%E5%A5%B3%E6%80%A7\n%E5%8A%B1%E5%BF%97\n%E6%91%84%E5%BD%B1\n%E6%95%99%E8%82%B2\n%E8%81%8C%E5%9C%BA\n%E7%BE%8E%E9%A3%9F\n%E6%B8%B8%E8%AE%B0\n%E7%81%B5%E4%BF%AE\n%E5%81%A5%E5%BA%B7\n%E6%83%85%E6%84%9F\n%E4%BA%BA%E9%99%85%E5%85%B3%E7%B3%BB\n%E4%B8%A4%E6%80%A7\n%E6%89%8B%E5%B7%A5\n%E5%85%BB%E7%94%9F\n%E5%AE%B6%E5%B1%85\n%E8%87%AA%E5%8A%A9%E6%B8%B8\n%E7%BB%8F%E6%B5%8E%E5%AD%A6\n%E7%AE%A1%E7%90%86\n%E7%BB%8F%E6%B5%8E\n%E5%95%86%E4%B8%9A\n%E9%87%91%E8%9E%8D\n%E6%8A%95%E8%B5%84\n%E8%90%A5%E9%94%80\n%E7%90%86%E8%B4%A2\n%E5%88%9B%E4%B8%9A\n%E8%82%A1%E7%A5%A8\n%E5%B9%BF%E5%91%8A\n%E4%BC%81%E4%B8%9A%E5%8F%B2\n%E7%AD%96%E5%88%92\n%E7%A7%91%E6%99%AE\n%E4%BA%92%E8%81%94%E7%BD%91\n%E7%A7%91%E5%AD%A6\n%E7%BC%96%E7%A8%8B\n%E4%BA%A4%E4%BA%92%E8%AE%BE%E8%AE%A1\n%E7%AE%97%E6%B3%95\n%E7%94%A8%E6%88%B7%E4%BD%93%E9%AA%8C\n%E7%A7%91%E6%8A%80\nweb\n%E4%BA%A4%E4%BA%92\n%E9%80%9A%E4%BF%A1\nUE\n%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C\nUCD\n%E7%A8%8B%E5%BA%8F\n"
    }
   ],
   "source": [
    "import urllib.parse\n",
    "\n",
    "content = douban_crawler.download('https://book.douban.com/tag/?view=type&icn=index-sorttags-all')\n",
    "tree = html.fromstring(content)\n",
    "tag_list = tree.xpath('//td/a')\n",
    "for tag in tag_list:\n",
    "    print(urllib.parse.quote(tag.text))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6-final"
  },
  "orig_nbformat": 2,
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}